/*! ******************************************************************************
 *
 * Pentaho
 *
 * Copyright (C) 2024 by Hitachi Vantara, LLC : http://www.pentaho.com
 *
 * Use of this software is governed by the Business Source License included
 * in the LICENSE.TXT file.
 *
 * Change Date: 2029-07-20
 ******************************************************************************/


package org.pentaho.platform.engine.services.solution;

import org.pentaho.platform.api.engine.IExecutionListener;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.api.engine.IOutputHandler;
import org.pentaho.platform.api.engine.IParameterProvider;
import org.pentaho.platform.api.engine.IPentahoRequestContext;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IPentahoUrlFactory;
import org.pentaho.platform.api.engine.IRuntimeContext;
import org.pentaho.platform.api.engine.ISolutionEngine;
import org.pentaho.platform.engine.core.output.SimpleOutputHandler;
import org.pentaho.platform.engine.core.solution.SimpleParameterProvider;
import org.pentaho.platform.engine.core.system.PentahoRequestContextHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.core.system.StandaloneSession;
import org.pentaho.platform.engine.services.BaseRequestHandler;
import org.pentaho.platform.util.web.SimpleUrlFactory;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// TODO sbarkdull, ton's of opportunity here for some refactoring to get rid of european reuse

public class SolutionHelper {

  /**
   * 
   * 
   * Runs an action sequence. This method uses the base URL set by the Application Context
   * 
   * @param description
   *          An identifier for this process. This is used for auditing and logging purposes only.
   * @param userId
   *          The user (or user agent) that is requesting this execution. This is used for auditing and logging and
   *          also can be used in action sequences (for example to filter data)
   * @param actionSequence
   *          Path to the action sequence file
   * @param parameters
   *          Parameters to be passed to the action sequence
   * @param outputStream
   *          The output stream for content generated by the action sequence. Can be null.
   * @return
   */
  public static ISolutionEngine execute( final String description, final String userId, final String actionSequence,
      final Map parameters, final OutputStream outputStream ) {
    StandaloneSession session = new StandaloneSession( userId );
    return SolutionHelper.execute( description, session, actionSequence, parameters, outputStream, null, true );
  }

  /**
   * 
   * 
   * Runs an action sequence. This method uses the base URL set by the Application Context
   * 
   * @param description
   *          An identifier for this process. This is used for auditing and logging purposes only.
   * @param session
   *          The user session that is requesting this execution. This is used for auditing and logging and also
   *          can be used in action sequences (for example to filter data)
   * @param actionSequence
   *          Path to the action sequence file
   * @param parameters
   *          Parameters to be passed to the action sequence
   * @param outputStream
   *          The output stream for content generated by the action sequence. Can be null.
   * @return
   */
  public static ISolutionEngine execute( final String description, final IPentahoSession session,
      final String actionSequence, final Map parameters, final OutputStream outputStream ) {
    return SolutionHelper.execute( description, session, actionSequence, parameters, outputStream, null, true );
  }

  /**
   * 
   * 
   * Runs an action sequence. This method uses the base URL set by the Application Context
   * 
   * @param description
   *          An identifier for this process. This is used for auditing and logging purposes only.
   * @param session
   *          The user session that is requesting this execution. This is used for auditing and logging and also
   *          can be used in action sequences (for example to filter data)
   * @param actionSequence
   *          Path to the action sequence file
   * @param parameters
   *          Parameters to be passed to the action sequence
   * @param outputStream
   *          The output stream for content generated by the action sequence. Can be null.
   * @param collateMessages
   *          Collate a messages list or not. Chose false for very large processes
   * @return
   */
  public static ISolutionEngine
  execute( final String description, final IPentahoSession session, final String actionSequence,
        final Map parameters, final OutputStream outputStream, final boolean collateMessages ) {
    return SolutionHelper.execute( description, session, actionSequence, parameters, outputStream, null,
        collateMessages );
  }

  private static ISolutionEngine execute( final String description, final IPentahoSession session,
      final String actionSequence, final Map parameters, final OutputStream outputStream,
      final IExecutionListener execListener, final boolean collateMessages ) {
    return SolutionHelper.execute( description, session, actionSequence, parameters, outputStream, execListener,
        collateMessages, true );
  }

  /**
   * 
   * 
   * Runs an action sequence. This method uses the base URL set by the Application Context
   * 
   * @param description
   *          An identifier for this process. This is used for auditing and logging purposes only.
   * @param session
   *          The user session that is requesting this execution. This is used for auditing and logging and also
   *          can be used in action sequences (for example to filter data)
   * @param actionSequence
   *          Path to the action sequence file
   * @param parameters
   *          Parameters to be passed to the action sequence
   * @param outputStream
   *          The output stream for content generated by the action sequence. Can be null.
   * @param execListener
   *          An execution listener for feedback during execution. Can be null.
   * @return
   */

  public static ISolutionEngine execute( final String description, final IPentahoSession session,
      final String actionSequence, final Map parameters, OutputStream outputStream,
      final IExecutionListener execListener, final boolean collateMessages, final boolean manageHibernate ) {

    if ( manageHibernate ) {
      PentahoSystem.systemEntryPoint();
    }
    ISolutionEngine solutionEngine = null;
    try {

      solutionEngine = PentahoSystem.get( ISolutionEngine.class, session );
      solutionEngine.init( session );
      solutionEngine.setlistener( execListener );

      SimpleParameterProvider parameterProvider = new SimpleParameterProvider( parameters );
      IPentahoRequestContext requestContext = PentahoRequestContextHolder.getRequestContext();
      String url = requestContext.getContextPath();
      // Modifications by Ezequiel Cuellar
      // Old code.
      // String baseUrl = PentahoSystem.getApplicationContext().getBaseUrl();
      // New code. Since the SubActionComponent is being instantiated below to return feedback
      // it is necesary to configure the baseUrl to include the ViewAction.
      Object actionUrlComponent = parameters.get( StandardSettings.ACTION_URL_COMPONENT );
      if ( ( actionUrlComponent != null ) && ( actionUrlComponent.toString().length() > 0 ) ) {
        url += actionUrlComponent.toString();
      } else {
        url += "ViewAction?"; //$NON-NLS-1$ 
      }

      HashMap<String, IParameterProvider> parameterProviderMap = new HashMap<String, IParameterProvider>();
      parameterProviderMap.put( IParameterProvider.SCOPE_REQUEST, parameterProvider );

      IPentahoUrlFactory urlFactory = new SimpleUrlFactory( url );

      String processName = description;
      boolean persisted = false;
      // for now, the messages list needs to be untyped since we may put exceptions as well as strings in it
      List<?> messages = null;

      if ( collateMessages ) {
        messages = new ArrayList();
      }

      if ( outputStream == null ) {
        outputStream = new ByteArrayOutputStream( 0 );
      }
      SimpleOutputHandler outputHandler = null;
      if ( outputStream != null ) {

        // Modifications by Ezequiel Cuellar
        // Old code.
        // outputHandler = new SimpleOutputHandler(outputStream, false);
        // New code. Without setting the allowFeedback parameter to true it is assumed that SubActionComponent
        // instances
        // are never capable of returning feedback which may not always be the case.
        outputHandler = new SimpleOutputHandler( outputStream, true );
        outputHandler.setOutputPreference( IOutputHandler.OUTPUT_TYPE_DEFAULT );
      }
      solutionEngine.execute( actionSequence, processName, false, true, null, persisted, parameterProviderMap,
          outputHandler, null, urlFactory, messages );

    } finally {
      if ( manageHibernate ) {
        PentahoSystem.systemExitPoint();
      }
    }
    return solutionEngine;

  }

  /**
   * doAction executes an action within the bi platform and returns true if successful.
   * 
   * Snagged from ChartHelper
   * 
   * @param solutionName
   *          the solution name
   * @param actionPath
   *          the action path
   * @param actionName
   *          the action name
   * @param processId
   *          the process id
   * @param parameterProvider
   *          the collection of parameters to customize the chart
   * @param outputStream
   *          the output object
   * @param userSession
   *          the user session object
   * @param messages
   *          a collection to store error and logging messages
   * @param logger
   *          logging object
   * 
   * @return the runtime context
   */
  public static boolean doAction( String actionPath, final String processId,
      final IParameterProvider parameterProvider, final OutputStream outputStream, final IPentahoSession userSession,
      final ArrayList messages, final ILogger logger ) {
    int status = IRuntimeContext.RUNTIME_STATUS_FAILURE;
    IRuntimeContext runtime = null;
    try {
      runtime =
          SolutionHelper.doActionInternal( actionPath, processId, parameterProvider, outputStream, userSession,
              messages, logger );
      if ( runtime != null ) {
        status = runtime.getStatus();
      }
    } finally {
      if ( runtime != null ) {
        runtime.dispose();
      }
    }
    return status == IRuntimeContext.RUNTIME_STATUS_SUCCESS;
  }

  /**
   * doAction executes an action within the bi platform and returns the runtime context.
   * 
   * Snagged from ChartHelper
   * 
   * @param solutionName
   *          the solution name
   * @param actionPath
   *          the action path
   * @param actionName
   *          the action name
   * @param processId
   *          the process id
   * @param parameterProvider
   *          the collection of parameters to customize the chart
   * @param userSession
   *          the user session object
   * @param messages
   *          a collection to store error and logging messages
   * @param logger
   *          logging object
   * 
   * @return the runtime context
   */
  public static IRuntimeContext doAction( final String actionPath, final String processId,
      final IParameterProvider parameterProvider, final IPentahoSession userSession, final ArrayList messages,
      final ILogger logger ) {
    return doActionInternal( actionPath, processId, parameterProvider, null, userSession, messages, logger );
  }

  private static IRuntimeContext doActionInternal( final String actionPath, final String processId,
      final IParameterProvider parameterProvider, final OutputStream outputStream, final IPentahoSession userSession,
      final ArrayList messages, final ILogger logger ) {
    SimpleOutputHandler outputHandler = new SimpleOutputHandler( outputStream, false );
    BaseRequestHandler requestHandler =
        new BaseRequestHandler( userSession, null, outputHandler, parameterProvider, null );

    requestHandler.setProcessId( processId );
    requestHandler.setActionPath( actionPath );

    return requestHandler.handleActionRequest( 0, 0 );
  }
}
